﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using Network.ThreadBase;

namespace Network.Tcp;

public abstract class TcpBase : ThreadBase.ThreadBase
{
    public TcpBase(string name)
        :base(name)
    {
    }
    public void SubscribeTcp(ITcpSubscriber tcpSubscriber)
    {
        TcpSubscriber = tcpSubscriber;
    }
    public void SetTimeOut(int milliSecond)
    {
        TimeOutMicroSecond = milliSecond * 1000;
    }
    public abstract bool Init();
    public void DisConnect(long sessionID)
    {
        lock (DisConnectSessionMutex)
        {
            DisConnectSessions.Add(sessionID);
        }
    }
    public bool Send(TcpEvent tcpEvent)
    {
        var connectData = GetConnectData(tcpEvent.SessionID);
        if (connectData == null || connectData.Socket == null || !connectData.Socket.Connected)
            return false;
        int totalSendLen = 0;
        int sendLen;
        while (totalSendLen < tcpEvent.Buff?.Length)
        {
            sendLen = connectData.Socket.Send(tcpEvent.Buff, totalSendLen, tcpEvent.Buff.Length - totalSendLen, SocketFlags.None);
            if (sendLen <= 0)
            {
                Console.WriteLine($"DisConnect For Send. SessionID:{tcpEvent.SessionID}, ErrorID:{sendLen}.");
                DisConnect(tcpEvent.SessionID);
                return false;
            }
            else
            {
                Console.WriteLine($"Tcp Send Len:{sendLen}");
                totalSendLen += sendLen;
            }
        }
        return true;
    }
    protected ConnectData? GetConnectData(long sessionID)
    {
        lock (ConnectDataMutex)
        {
            return ConnectDatas.Where(connectData => connectData.SessionID == sessionID).FirstOrDefault();
        }
    }
    protected ConnectData? GetConnectData(Socket socket)
    {
        lock (ConnectDataMutex)
        {
            return ConnectDatas.Where(connectData => connectData.Socket == socket).FirstOrDefault();
        }
    }
    protected virtual void AddConnect(ConnectData connectData)
    {
        lock (ConnectDataMutex)
        {
            ConnectDatas.Add(connectData);
        }
        if (TcpSubscriber != null)
        {
            TcpSubscriber.OnConnected(connectData);
        }
    }
    protected virtual void RemoveConnect(ConnectData connectData)
    {
        lock (ConnectDataMutex)
        {
            ConnectDatas.Remove(connectData);
        }
        TcpSubscriber?.OnDisConnected(connectData);
        connectData.Socket?.Close();
    }
    protected void DoDisConnect()
    {
        lock (DisConnectSessionMutex)
        {
            foreach (long sessionID in DisConnectSessions)
            {
                var connectData = GetConnectData(sessionID);
                if (connectData != null)
                {
                    RemoveConnect(connectData);
                }
            }
            DisConnectSessions.Clear();
        }
    }
    protected void DoRecv(Socket socket)
    {
        var connectData = GetConnectData(socket);
        if (connectData == null)
        {
            Console.WriteLine($"GetConnectData Failed. Socket:{socket.Handle}");
            return;
        }
        TcpEvent tcpEvent = new TcpEvent();
        tcpEvent.SessionID = connectData.SessionID;
        tcpEvent.IPAddress = connectData.IPEndPoint;
        tcpEvent.Buff = new byte[TcpEvent.BuffSize];
        try
        {
            int len = socket.Receive(tcpEvent.Buff, tcpEvent.ReadIndex, TcpEvent.BuffSize - tcpEvent.ReadIndex, SocketFlags.None);
            if (len <= 0)
            {
                Console.WriteLine($"Close Socket For Receive Failed. len:{len}");
                RemoveConnect(connectData);
            }
            else
            {
                Console.WriteLine($"Tcp OnRecv Len:{len}");
                tcpEvent.Length = len;
                TcpSubscriber?.OnRecv(tcpEvent);
            }
        }
        catch (SocketException ex)
        {
            Console.WriteLine($"Receive Failed. {ex.Message}");
            RemoveConnect(connectData);
        }
    }
    protected abstract void HandleTcpEvent();
    public override void ThreadExit()
    {
        base.ThreadExit();

        foreach (var connectData in ConnectDatas)
        {
            TcpSubscriber?.OnDisConnected(connectData);
            connectData.Socket?.Close();
        }
        ConnectDatas.Clear();

        DisConnectSessions.Clear();
    }
    protected override void Run()
    {
        HandleTcpEvent();
    }

    protected ITcpSubscriber? TcpSubscriber;
    protected int TimeOutMicroSecond;

    protected readonly object DisConnectSessionMutex = new object();
    protected List<long> DisConnectSessions = new List<long>();

    protected readonly object ConnectDataMutex = new object();
    protected List<ConnectData> ConnectDatas = new List<ConnectData>();
}
